package MLP
{
	import UIUtils.uif_util;
	
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.DisplayObject;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.MouseEvent;
	import flash.events.TimerEvent;
	import flash.filters.BlurFilter;
	import flash.geom.Matrix;
	import flash.geom.Matrix3D;
	import flash.geom.Point;
	import flash.geom.Rectangle;
	import flash.geom.Vector3D;
	import flash.utils.Timer;
	
	public class multiLayerObject extends Sprite {
		private var _back				: Bitmap;
		private var _layer3D			: mlp_layer3d;
		private var _mPosition			: Point;
		private var _waveIndex1			: Number;
		private var _waveIndex2			: Number;
		private var _size				: Point;
		private var _curPos				: Point; 
		private var _activeTime			: Timer;
		private static const _density	: Number = 0.07;
		
		public function multiLayerObject() {
			super();
			_waveIndex1 = 0;
			_waveIndex2 = 0;
			_size		= new Point();
			_back		= new Bitmap();
			_layer3D 	= new mlp_layer3d();
			_activeTime	= new Timer(2000, 1);
			
			addChild(_back);
			addChild(_layer3D);

			if (stage) listenEvents()
			else addEventListener(Event.ADDED_TO_STAGE, doStage); 
		}
		
		public static function totalBounds(a_bitmaps: Array): Rectangle {
			var rect: Rectangle = new Rectangle();
			a_bitmaps.forEach(function(item: Object, i: int, arr: Array): void {
				if (item is DisplayObject)
					rect = rect.union(new Rectangle(item.x, item.y, item.width, item.height));
			});
			return rect;
		}
		
		private function doStage(e: Event): void {
			removeEventListener(Event.ADDED_TO_STAGE, doStage);
			
			_mPosition 	=  new Point(mouseX, mouseY);
			_curPos		= _mPosition.add(new Point(0.01, 0.01));
			updateMatrix();
			listenEvents();
		}
		
		private function doStageRemove(e: Event): void {
			listenEvents();
		}
		
		protected function listenEvents(): void {
			stage.addEventListener(Event.ENTER_FRAME, doFrame);
			stage.addEventListener(MouseEvent.MOUSE_MOVE, doMouseMove);
			_activeTime.addEventListener(TimerEvent.TIMER_COMPLETE, doActiveTimer);
			addEventListener(Event.REMOVED_FROM_STAGE, doStageRemove);
		}
		
		protected function removeEvents(): void {
			stage.removeEventListener(Event.ENTER_FRAME, doFrame);
			stage.removeEventListener(MouseEvent.MOUSE_MOVE, doMouseMove);
			_activeTime.removeEventListener(TimerEvent.TIMER_COMPLETE, doActiveTimer);
			removeEventListener(Event.REMOVED_FROM_STAGE, doStageRemove);
		}
		
		protected function doMouseMove(e: MouseEvent): void {
			_curPos = new Point(mouseX, mouseY);
/*			
			if (_activeTime.running) _activeTime.stop();
			_activeTime.start();
*/			
		}
		
		private function get centerStage(): Point {
			return new Point(stage.stageWidth / 2, stage.stageHeight / 2);
		}
		
		private function doActiveTimer(e: TimerEvent): void {
			_curPos = centerStage;
		}
		
		protected function doFrame(e: Event): void {
			updateMatrix();
			_waveIndex1 += 0.075;
			_waveIndex2 += 0.05;
		}
		
		public function get center(): Point {
			return new Point(width / 2, height / 2);
		}
		
		protected function updateMatrix(): void {
			var matrix: Matrix3D = new Matrix3D();
			var c: Point = center;
			matrix.appendTranslation(-c.x, -c.y, 0);
			
			updateFromCursor(matrix);
			updateFromWave(matrix);
			
			matrix.appendTranslation(c.x, c.y, 0);				
			_layer3D.transform.matrix3D = matrix;
		}
		
		protected function updateFromWave(matrix: Matrix3D): void {
			var _wave: Number = (Math.sin(_waveIndex1) + Math.sin(_waveIndex2));
			matrix.appendRotation(_wave, Vector3D.Y_AXIS);
		}
		
		protected function updateFromCursor(matrix: Matrix3D): void {
			var delta: Point = new Point((_curPos.x - _mPosition.x) * _density, (_curPos.y - _mPosition.y) * _density);
			if (delta.length) {
				_mPosition = new Point(_mPosition.x + delta.x, _mPosition.y + delta.y);
				var c: Point = center;
				var k: Number = -0.016;
				matrix.appendRotation((_mPosition.x - c.x) * k, Vector3D.Y_AXIS);
				matrix.appendRotation((_mPosition.y - c.y) * k, Vector3D.X_AXIS);
			}
		}
		
		public function addLayers(a_layers: Array, a_depths: Object=0): void {
			var bounds: Rectangle = totalBounds(a_layers);
			_layer3D.addLayers(a_layers, a_depths);
			if (_back.bitmapData) _back.bitmapData.dispose();

			if (_layer3D.numChildren > 0) {
				var bk: DisplayObject = _layer3D.getChildAt(0);
				var mat: Matrix = new Matrix(bounds.width/bk.width, 0, 0, bounds.height/bk.height);
				_back.bitmapData = new BitmapData(bounds.width, bounds.height, false);
				_back.bitmapData.draw(bk, mat);
				_back.bitmapData.applyFilter(_back.bitmapData, _back.bitmapData.rect, uif_util.NULLPOINT, new BlurFilter(4, 4, 3));
			}
			resize(bounds.width, bounds.height);
		}
		
		public function dispose(): void {
			if (_back.bitmapData) _back.bitmapData.dispose();
			if (parent) parent.removeChild(this);
		}
		
		public function resize(a_width: int, a_height: int): void {
			_size.x = a_width;
			_size.y = a_height;
		}
		
		override public function get width():Number {
			return _size.x;
		}
		
		override public function get height():Number {
			return _size.x;
		}
		
		public function get layer3D(): Sprite {
			return _layer3D;
		}
	}
}